export { createHttpResponsePage }
export { createHttpResponsePageContextJson }
export { createHttpResponseErrorFallback }
export { createHttpResponseErrorFallback_noGlobalContext }
export { createHttpResponseRedirect }
export { createHttpResponse404 }
export { createHttpResponseBaseIsMissing }
export type { HttpResponse }

import type { GetPageAssets } from './getPageAssets.js'
import { assert, assertWarning, escapeHtml } from '../../utils.js'
import type { HtmlRender } from './html/renderHtml.js'
import { getErrorPageId, isErrorPage } from '../../../shared-server-client/error-page.js'
import type { RenderHook } from './execHookOnRenderHtml.js'
import type { RedirectStatusCode, AbortStatusCode, UrlRedirect } from '../../../shared-server-client/route/abort.js'
import { getHttpResponseBody, getHttpResponseBodyStreamHandlers, HttpResponseBody } from './getHttpResponseBody.js'
import { getEarlyHints, type EarlyHint } from './getEarlyHints.js'
import { assertNoInfiniteHttpRedirect } from './createHttpResponse/assertNoInfiniteHttpRedirect.js'
import type { PageContextBegin } from '../renderPageServer.js'
import type { GlobalContextServerInternal } from '../globalContext.js'
import { resolveHeadersResponseFinal } from './headersResponse.js'

type HttpResponse = {
  statusCode: 200 | 404 | 500 | RedirectStatusCode | AbortStatusCode
  headers: [string, string][]
  earlyHints: EarlyHint[]
  // We don't use @deprecated to avoid TypeScript to remove the JSDoc
  /** **Deprecated**: use `headers` instead, see https://vike.dev/migration/0.4.134 */
  contentType: 'application/json' | 'text/html;charset=utf-8'
} & HttpResponseBody

// Trick to improve TypeScript DX
type StatusCode = HttpResponse['statusCode']
type ContentType = HttpResponse['contentType']
type ResponseHeaders = HttpResponse['headers']

async function createHttpResponsePage(
  htmlRender: HtmlRender,
  renderHook: null | RenderHook,
  pageContext: {
    pageId: null | string
    is404: null | boolean
    errorWhileRendering: null | Error
    __getPageAssets: GetPageAssets
    _globalContext: GlobalContextServerInternal
    abortStatusCode?: AbortStatusCode
    headersResponse?: Headers
  },
): Promise<HttpResponse> {
  let statusCode: StatusCode | undefined = pageContext.abortStatusCode
  if (!statusCode) {
    const isError = !pageContext.pageId || isErrorPage(pageContext.pageId, pageContext._globalContext._pageConfigs)
    if (pageContext.errorWhileRendering) {
      assert(isError)
    }
    if (!isError) {
      assert(pageContext.is404 === null)
      statusCode = 200
    } else {
      assert(pageContext.is404 === true || pageContext.is404 === false)
      statusCode = pageContext.is404 ? 404 : 500
    }
  }

  const earlyHints = getEarlyHints(await pageContext.__getPageAssets())
  const headers = resolveHeadersResponseFinal(pageContext, statusCode)
  return createHttpResponse(statusCode, 'text/html;charset=utf-8', headers, htmlRender, earlyHints, renderHook)
}

function createHttpResponse404(errMsg404: string): HttpResponse {
  const httpResponse = createHttpResponse(
    404,
    'text/html;charset=utf-8',
    [],
    `<p>${errMsg404}.</p><script>console.log('This HTML was generated by Vike.')</script>`,
  )
  return httpResponse
}

function createHttpResponseBaseIsMissing(urlOriginal: string, baseServer: string): HttpResponse {
  const httpResponse = createHttpResponse(
    // We use the error code `500` to signal a failing state because this HTTP response should never be used, see https://vike.dev/base-url#setup
    // In other words: this HTTP response is expected to be generated but isn't expected to be actually used.
    500,
    'text/html;charset=utf-8',
    [],
    `
<h1>Error: Base URL is missing</h1>
<p>
  <a href="https://vike.dev/renderPage"><code>renderPage(pageContextInit)</code></a> called with <code>pageContextInit.urlOriginal===${JSON.stringify(urlOriginal)}</code> which doesn't start with the Base URL <code>${baseServer}</code>.
</p>
<p>
  See <a href="https://vike.dev/base-url#setup">vike.dev/base-url#setup</a> for how to properly setup your server while using a Base URL.
</p>
<style>
  code {
    font-family: monospace;
    background-color: #eaeaea;
    padding: 3px 5px;
    border-radius: 4px;
  }
</style>
`,
  )
  return httpResponse
}
function createHttpResponseErrorFallback(pageContext: {
  _globalContext: GlobalContextServerInternal
}) {
  const reason = (() => {
    const errorPageId = getErrorPageId(
      pageContext._globalContext._pageFilesAll,
      pageContext._globalContext._pageConfigs,
    )
    if (errorPageId) {
      return "the error page (https://vike.dev/error-page) couldn't be rendered (for example if an error occurred while rendering the error page)" as const
    } else {
      return 'no error page (https://vike.dev/error-page) is defined, make sure to create one' as const
    }
  })()
  return createHttpResponseError_(reason)
}
function createHttpResponseErrorFallback_noGlobalContext() {
  return createHttpResponseError_('no error page (https://vike.dev/error-page) could be rendered')
}
function createHttpResponseError_(reason: string): HttpResponse {
  const httpResponse = createHttpResponse(
    500,
    'text/html;charset=utf-8',
    [],
    `<p>An error occurred.</p><script>console.log(${JSON.stringify(
      `This HTML was generated by Vike. Vike returned this HTML because ${reason}.`,
    )})</script>`,
  )
  return httpResponse
}

async function createHttpResponsePageContextJson(pageContextSerialized: string) {
  const httpResponse = createHttpResponse(200, 'application/json', [], pageContextSerialized, [], null)
  return httpResponse
}

function createHttpResponseRedirect({ url, statusCode }: UrlRedirect, pageContextInit: PageContextBegin): HttpResponse {
  assertNoInfiniteHttpRedirect(url, pageContextInit)
  assert(url)
  assert(statusCode)
  assert(300 <= statusCode && statusCode <= 399)
  const headers: ResponseHeaders = [['Location', url]]
  return createHttpResponse(
    statusCode,
    'text/html;charset=utf-8',
    headers,
    // For bots / programmatic crawlig: show what's going on.
    // For users: showing a blank page is probably better than a flickering text.
    `<p style="display: none">Redirecting to ${escapeHtml(
      url,
    )}</p><script>console.log('This HTML was generated by Vike.')</script>`,
  )
}

function createHttpResponse(
  statusCode: StatusCode,
  contentType: ContentType,
  headers: ResponseHeaders,
  htmlRender: HtmlRender,
  earlyHints: EarlyHint[] = [],
  renderHook: null | RenderHook = null,
): HttpResponse {
  headers.push(['Content-Type', contentType])

  assert(renderHook || typeof htmlRender === 'string')
  return {
    statusCode,
    headers,
    // TO-DO/next-major-release: remove
    get contentType() {
      assertWarning(
        false,
        'pageContext.httpResponse.contentType is deprecated and will be removed in the next major release. Use pageContext.httpResponse.headers instead, see https://vike.dev/migration/0.4.134',
        { onlyOnce: true },
      )
      return contentType
    },
    earlyHints,
    get body() {
      return getHttpResponseBody(htmlRender, renderHook)
    },
    ...getHttpResponseBodyStreamHandlers(htmlRender, renderHook),
  }
}
